/*
 * Region.cpp
 *
 *  Created on: Jun 2, 2011
 *      Author: hongyang
 */

#include "Region.h"
#include "../smartgame/SgNbIterator.h"

namespace{
static const bool DEBUG_REGION = false;
}

int Region::alloc_num = 0;

Region::Region(const Board& b, SgBlackWhite c) :
	board(b), player(c), id(alloc_num++) {
}

Region::~Region() {
}

bool Region::isInterior(const Block* block) const {
	SG_ASSERT(blocks.Contains(block));
	const SgBlackWhite opp = SgOppBW(block->color());
	for (StoneIterator it(block->getPoints()); it; ++it)
		for (SgNb4Iterator nb(*it); nb; ++nb) {
			const SgPoint p = *nb;
			if (board.isColor(p, opp))
				return false;
			if (board.isEmpty(p)) {
				SgPointSet set = board.findInDir(*it, p);
				if (board.containsColor(set, opp) > 0)
					return false;
				if (board.allColor(set, SG_EMPTY)) {
					SgPointSet set2 = set.Border8(SG_MAX_SIZE);
					if (board.containsColor(set, opp))
						return false;
					if (board.containsColor(set, block->color()) < 2)
						return false;
				}
			}
		}
	return true;
}

SgPointSet Region::boundary(bool pickColor) const {
	SgPointSet set;
	int isDen = board.stage();
	SgBlackWhite opp = SgBW(player);
	for (SgVectorIteratorOf<Block> sit(blocks); sit; ++sit) {
		Block* b = *sit;
		if (isBoundary(b)) {
			SgPointSet s = b->pointSet().Border(SG_MAX_SIZE);
			SgPointSet all = b->pointSet().Border8(SG_MAX_SIZE);
			SgPointSet diag = all - s;
			for (SgSetIterator sit(s); sit; ++sit) {
				SgPoint p = *sit;
				if (board.isColor(p, opp) && pickColor)
					continue;
				set.Include(p);
			}
			for (SgSetIterator sit(diag); sit; ++sit) {
				SgPoint p = *sit;
				if (board.isColor(p, opp) && pickColor)
					continue;
				SgPointSet neigh;
				neigh.Include(p);
				neigh |= neigh.Border(SG_MAX_SIZE);
				if (board.containsColor(neigh, opp) == 0)
					set.Include(p);
			}
		}
	}
	return set;
}

bool Region::isConnected() const {
	// TODO use strength to check if the region is connected.
	return true;
}

void Region::computeSingleBlockEyeSpace()
{
	SgPointSet points = area();
    const int nu = points.Size();
    SG_ASSERT(nu > 0);

    if (DEBUG_REGION) SgDebug()<< "compute Single Block Eye Space begin"<<"\n";

    if (nu <= 2)
        eyeCount.SetEyes(1, 1);
    else{
        bool isNakade = false, makeNakade = false, maybeSeki = false;
        bool sureSeki = false;
        bool makeFalse = false;
        if (DEBUG_REGION) SgDebug()<< "test nakede begin"<<"\n";
        if (nu <= 7){
            SgPoint vitalP(SG_NULLPOINT);
            GoEyeUtil::TestNakade(points, board, player, true,
                       isNakade, makeNakade, makeFalse,
                       maybeSeki, sureSeki,
                       &vitalP);
        }
        if (DEBUG_REGION) SgDebug()<< "test nakede end"<<"\n";
        if (sureSeki)
        	eyeCount.SetLocalSeki();
        else if (maybeSeki)
        {
        	eyeCount.SetMinEyes(0);
        	eyeCount.SetMaxEyes(2);
            int potEyes = isNakade ? 1 : 2;
            eyeCount.SetExactPotEyes(potEyes);
            eyeCount.SetMaybeLocalSeki();
        }
        else if (isNakade || makeNakade)
        {
            int potEyes = isNakade ? 1 : 2;
            eyeCount.SetEyes(1, potEyes);
        }
        else if (makeFalse)
        	eyeCount.SetEyes(0, 1);

        else // @todo: huge areas without opp. are alive, at least seki,
             // possible eye space if filled by safe opp, etc.
        {
        	eyeCount.SetMinEyes(0);
        	eyeCount.SetMaxEyes(2);
        	eyeCount.SetExactPotEyes(2);
        }
    }

    if (DEBUG_REGION) SgDebug()<< "compute Single Block Eye Space end"<<"\n";
}

void Region::computeMultipleBlockEyeSpace()
{
    SG_ASSERT(blocks.MinLength(2));
	SgPointSet points = area();
    const int nu = points.Size();
    SG_ASSERT (nu > 0);

    if (DEBUG_REGION) SgDebug()<< "compute multiple Block Eye Space begin"<<"\n";

    int minNuEyes = 0;

    bool isNakade = false;
    bool makeNakade = false;
    bool makeFalse = false;

    if (nu <= 2)
    {
        if (minNuEyes == 1)
        	eyeCount.SetEyes(1, 1);
        return;
    }
    else if (nu <= 7)
    {
        if (DEBUG_REGION) SgDebug()<< "test nakade begin"<<"\n";

        SgPoint vitalP(SG_NULLPOINT);
        bool maybeSeki = false, sureSeki = false;
        GoEyeUtil::TestNakade(points, board, player, true,
                   isNakade, makeNakade,
                   makeFalse,
                   maybeSeki, sureSeki,
                   &vitalP);

        if (DEBUG_REGION) SgDebug()<< "test nakade end"<<"\n";

    }
    if (makeFalse)
    	eyeCount.SetEyes(0, 1);
    else if (isNakade)
    	eyeCount.SetEyes(1, 1);
    else if (makeNakade)
    	eyeCount.SetEyes(1, 2);
    else // todo: huge areas without opp. are alive, at least seki,
         // possible eye space if filled by safe opp, etc.
    {
    	eyeCount.SetMinEyes(minNuEyes);
    	eyeCount.SetMaxEyes(2);
    	eyeCount.SetExactPotEyes(2);
    }

    if (DEBUG_REGION) SgDebug()<< "compute multiple Block Eye Space end"<<"\n";
}

void Region::computeStrength(){
	SgPointSet points = area();
	strength = 0;
	for (int x=1; x<=SG_MAX_SIZE; ++x)
		for (int y=1; y<=SG_MAX_SIZE; ++y){
			SgPoint point = SgPointUtil::Pt(x, y);
			if (board.getColor(point) == SG_EMPTY)
				for (SgSetIterator it(points); it; ++it)
					if (SgPointUtil::AreAdjacent(point, *it)){
						++strength;
						break;
					}
		}
}

int Region::getStrength(){
	computeStrength();
	return strength;
}

void Region::computeEyeSpace()
{
	if (DEBUG_REGION) SgDebug()<< "compute eye begin" <<"\n";
    if (blocks.IsLength(1))
        computeSingleBlockEyeSpace();
    else
        computeMultipleBlockEyeSpace();
}

bool Region::enoughEyes(){

}
